// Copyright 2010 Abhishek Kulkarni. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goprop

/*
 A "Cell" is a stateful entity which remembers (partial or complete)
information in a propagator network. It is roughly analagous to a
"memory cell" in that it stores information, but is distinguished 
by its ability to merge partial information from multiple sources
and alerting propagators dependent on its value when the value
changes. Cells, thus, allow multi-directional information flow 
in the propagation network.

Cells are implemented as goroutines which continually listen for
"commands" on a channel, and exchange data over a "bus". It
maintains a map of neighboring propagators to alert when it has
"new" information.

*/

type Cell struct {
	Name      string
	Network   *PropagatorNetwork
	SChan     chan Msg
	Value     interface{}
	State     States
	Neighbors map[string]*Propagator
}

// make the cell to listen for commands to process
func (c *Cell) Listen() {
	for {
		msg := <-c.SChan
		if msg.Cmd == Stop {
			c.State = Stopped
			return
		}
		go func() {
			switch msg.Cmd {
			default:
				defer Error("unknown cmd %d\n", msg.Cmd)

			case AddContent:
				val := c.Value
				c.Value = msg.Value

				if val != nil {
					newval := merger(val, c.Value)
					c.Value = newval
				}

				if (val != c.Value) && (len(c.Neighbors) > 0) {
					for _, p := range c.Neighbors {
						Debug(dbgCell, "** alerting %s with content %#v\n", p.Name, c.Value)
						p.Alert()
					}
				}

			case Content:
				rchan := msg.Value
				rchan.(chan interface{}) <- Msg{Cmd: Content, Value: c.Value}
			}
		}()
	}
}


// add or retrive information to/from a cell
func (c *Cell) Content(value ...interface{}) interface{} {
	switch len(value) {
	case 0:
		rchan := make(chan interface{})
		c.SChan <- Msg{Cmd: Content, Value: rchan}
		msg := <-rchan
		Debug(dbgCell, "** retrieved content %#v from cell %s\n", msg.(Msg).Value, c.Name)
		return msg.(Msg).Value
	case 1:
		c.SChan <- Msg{Cmd: AddContent, Value: value[0]}
		Debug(dbgCell, "** added content %#v to cell %s\n", value[0], c.Name)
		<-c.Network.Quiescent()
		return value[0]
	default:
		return nil
	}
	return nil
}

// add a new neighbor (propagator) to a cell
func (c *Cell) NewNeighbor(p *Propagator) {

	if _, present := c.Neighbors[p.Name]; present {
		Debug(dbgPropagator, ">> propagator %s is already a neighbor of cell %s", p.Name, c.Name)
		return
	}

	Debug(dbgCell, "** new neighbour %s to cell %s\n", p.Name, c.Name)
	c.Neighbors[p.Name] = p
}

// stop a cell
func (c *Cell) Destroy() {
	if c.State != Stopped {
		c.SChan <- Msg{Cmd: Stop}
	}
}
